<!DOCTYPE html>
<html lang="zh">
<head><meta name="generator" content="Hexo 3.9.0">
    <meta charset="utf-8">
<title>JDK动态代理的深入理解 - 听到微笑的博客</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">



    <meta name="description" content="​    代理模式是应用框架中经常使用的一种模式，动态代理是AOP（面向切面编程）思想的一种重要的实现方式，在我们常用的框架中也经常遇见代理模式的身影">
<meta name="keywords" content="设计模式,代理模式">
<meta property="og:type" content="article">
<meta property="og:title" content="JDK动态代理的深入理解">
<meta property="og:url" content="http://www.tianjindong.cn/article/43606.html">
<meta property="og:site_name" content="听到微笑的博客">
<meta property="og:description" content="​    代理模式是应用框架中经常使用的一种模式，动态代理是AOP（面向切面编程）思想的一种重要的实现方式，在我们常用的框架中也经常遇见代理模式的身影">
<meta property="og:locale" content="zh-CN">
<meta property="og:image" content="http://www.tianjindong.cn/gallery/thumbnails/b6.jpg">
<meta property="og:updated_time" content="2020-08-22T06:32:33.754Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="JDK动态代理的深入理解">
<meta name="twitter:description" content="​    代理模式是应用框架中经常使用的一种模式，动态代理是AOP（面向切面编程）思想的一种重要的实现方式，在我们常用的框架中也经常遇见代理模式的身影">
<meta name="twitter:image" content="http://www.tianjindong.cn/gallery/thumbnails/b6.jpg">







<link rel="icon" href="/images/favicon.ico">


<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.7.2/css/bulma.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Ubuntu:400,600|Source+Code+Pro">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@9.12.0/styles/atom-one-dark.css">


    
    
    
    <style>body>.footer,body>.navbar,body>.section{opacity:0}</style>
    

    
    
    
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lightgallery@1.6.8/dist/css/lightgallery.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/css/justifiedGallery.min.css">
    

    
    

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/outdatedbrowser@1.1.5/outdatedbrowser/outdatedbrowser.min.css">


    
    
    
    

<link rel="stylesheet" href="/css/back-to-top.css">


    
    

    
    
<script>
var _hmt = _hmt || [];
(function() {
    var hm = document.createElement("script");
    hm.src = "//hm.baidu.com/hm.js?81f9c1489f84c7a99b0bd377c9fb5de8";
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(hm, s);
})();
</script>

    
    

    
    
<link rel="stylesheet" href="/css/progressbar.css">
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

    
    
    

    
    
    


<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/css/iconfont.css">
</head>
<body class="is-2-column">
    <nav class="navbar navbar-main">
    <div class="container">
        <div class="navbar-brand is-flex-center">
            <a class="navbar-item navbar-logo" href="/">
            
                <img src="/images/logo.png" alt="JDK动态代理的深入理解" height="28">
            
            </a>
        </div>
        <div class="navbar-menu">
            
            <div class="navbar-start">
                
                <a class="navbar-item" href="/">主页</a>
                
                <a class="navbar-item" href="/categories">分类</a>
                
                <a class="navbar-item" href="/tags">标签</a>
                
                <a class="navbar-item" href="/archives">历史</a>
                
                <a class="navbar-item" href="/music.html">音乐</a>
                
                <a class="navbar-item" href="/about.html">关于</a>
                
            </div>
            
            <div class="navbar-end">
                
                
                <a class="navbar-item is-hidden-tablet catalogue" title="目录" href="javascript:;">
                    <i class="fas fa-list-ul"></i>
                </a>
                
                
                <a class="navbar-item search" title="搜索" href="javascript:;">
                    <i class="fas fa-search"></i>
                </a>
                
            </div>
        </div>
    </div>
</nav>
        
    <section class="section">
        <div class="container">
            <div class="columns">
                <div class="column is-8-tablet is-9-desktop is-9-widescreen has-order-2 column-main"><div class="card">
    
    <div class="card-image">
        <span class="image is-7by1">
            <img class="thumbnail" src="/gallery/thumbnails/b6.jpg" alt="JDK动态代理的深入理解">
        </span>
    </div>
    
    <div class="card-content article ">
        
        <div class="level article-meta is-size-7 is-uppercase is-mobile is-overflow-x-auto">
            <div class="level-left">
                <time class="level-item has-text-grey" datetime="2019-07-16T07:37:47.000Z">2019-07-16</time>
                
                <div class="level-item">
                <a class="has-link-grey -link" href="/categories/设计模式/">设计模式</a>
                </div>
                
                
                <span class="level-item has-text-grey">
                    
                    
                    18 分钟 读完 (大约 2670 个字)
                </span>
                
                
            </div>
        </div>
        
        <h1 class="title is-size-3 is-size-4-mobile has-text-weight-normal">
            
                JDK动态代理的深入理解
            
        </h1>
        <div class="content">
            <p>​    代理模式是应用框架中经常使用的一种模式，动态代理是AOP（面向切面编程）思想的一种重要的实现方式，在我们常用的框架中也经常遇见代理模式的身影<a id="more"></a>，例如在Spring中事务管理就运用了动态代理，它将Service层原先应该进行的事务管理交给了Spring框架，大大简化了开发流程。在Hibernate中对象的懒加载模式，也运用了JDK的动态代理以及cglib代理。</p>
<h2 id="静态代理"><a href="#静态代理" class="headerlink" title="静态代理"></a><strong>静态代理</strong></h2><p>​    在说动态代理之前，我们需要先了解一下静态代理。</p>
<p>​    静态代理通常用于对原有业务逻辑的扩充。比如持有第三方jar包中的某个类，并调用了其中的某些方法。然后出于某种原因，比如记录日志、打印方法执行时间，但是又不好将这些逻辑写入第三方jar包的方法里。所以可以创建一个代理类实现和这个类方法相同的方法，通过让代理类持有真实对象，然后在原代码中调用代理类方法，来达到添加我们需要业务逻辑的目的。</p>
<p>​    这其实也就是代理模式的一种实现，通过对真实对象的封装，来实现扩展性。</p>
<h2 id="满足代理模式应用场景"><a href="#满足代理模式应用场景" class="headerlink" title="满足代理模式应用场景"></a><strong>满足代理模式应用场景</strong></h2><ol>
<li><p>两个角色：被代理者、执行者</p>
</li>
<li><p>注重过程：被代理对象不想做或者不方便做，但是必须要做时使用代理模式</p>
</li>
<li><p>代理对象：执行者必须持有被代理对象的引用</p>
</li>
</ol>
<p>这里我们提出一个需求：我们在编写的Service类的每个方法中没有进行事务处理，但根据需要我们必须进行实物处理，那此时我们进行静态代理。</p>
<p><strong>ServiceInterface接口:</strong></p>
<figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ServiceInterface</span> </span>&#123;</span><br><span class="line">	<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">a</span><span class="hljs-params">()</span></span>;</span><br><span class="line">	<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">b</span><span class="hljs-params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>接口的实现类：</strong></p>
<figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ServiceInterface</span></span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="hljs-meta">@Override</span></span><br><span class="line">	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">a</span><span class="hljs-params">()</span> </span>&#123;</span><br><span class="line">		System.out.println(<span class="hljs-string">"执行a方法"</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="hljs-meta">@Override</span></span><br><span class="line">	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">b</span><span class="hljs-params">()</span> </span>&#123;</span><br><span class="line">		System.out.println(<span class="hljs-string">"执行b方法"</span>);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>代理类:</strong></p>
<figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ServiceImplProxy</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ServiceInterface</span></span>&#123;</span><br><span class="line">	<span class="hljs-keyword">private</span> ServiceInterface service;</span><br><span class="line">        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ServiceImplProxy</span><span class="hljs-params">(ServiceInterface service)</span> </span>&#123;</span><br><span class="line">		<span class="hljs-keyword">super</span>();</span><br><span class="line">		<span class="hljs-keyword">this</span>.service = service;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="hljs-meta">@Override</span></span><br><span class="line">	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">a</span><span class="hljs-params">()</span> </span>&#123;</span><br><span class="line">		System.out.println(<span class="hljs-string">"开启事务"</span>);</span><br><span class="line">		service.a();</span><br><span class="line">		System.out.println(<span class="hljs-string">"提交事务"</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="hljs-meta">@Override</span></span><br><span class="line">	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">b</span><span class="hljs-params">()</span> </span>&#123;</span><br><span class="line">		System.out.println(<span class="hljs-string">"开启事务"</span>);</span><br><span class="line">		service.b();</span><br><span class="line">		System.out.println(<span class="hljs-string">"提交事务"</span>);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>测试：</strong></p>
<figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestProxy</span> </span>&#123;</span><br><span class="line">	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;</span><br><span class="line">		<span class="hljs-comment">//创建目标对象</span></span><br><span class="line">		ServiceInterface service=<span class="hljs-keyword">new</span> ServiceImpl();</span><br><span class="line">		<span class="hljs-comment">//创建代理对象，将目标对象传入代理对象</span></span><br><span class="line">		ServiceInterface serviceProxy =<span class="hljs-keyword">new</span> ServiceImplProxy(service);</span><br><span class="line">		</span><br><span class="line">		serviceProxy.a();</span><br><span class="line">		serviceProxy.b();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="静态代理的优点及缺点"><a href="#静态代理的优点及缺点" class="headerlink" title="静态代理的优点及缺点"></a><strong>静态代理的优点及缺点</strong></h2><p>​    上面演示了静态代理的完整过程，现在我们来看看静态代理的优缺点。</p>
<p>​    <strong>优点：</strong>扩展原功能，不侵入原代码。</p>
<p>​    <strong>缺点：</strong></p>
<ol>
<li><p>如果我们只需要调用代理对象的某一个方法，换句话说我们只需要代理对象代理委托类的某一个方法时，我们仍然需要实现接口中所有的方法，这样显得很浪费。当然在这里我们可以通过继承来实现静态代理，使用继承时，我们只需要覆写需要代理的方法即可。</p>
</li>
<li><p>如果在接口中定义了很多方法，而这些方法都需要被代理，并且代理的逻辑都是相同的，比如说在WEB开发中Service层所有方法执行之前都需要打开事务，结束后关闭事务。那么此时我们使用静态代理，会导致大量代码重复（想想如果有50个方法，你去一个一个手敲吧）。</p>
</li>
</ol>
<h2 id="动态代理"><a href="#动态代理" class="headerlink" title="动态代理"></a><strong>动态代理</strong></h2><h3 id="动态代理的原理："><a href="#动态代理的原理：" class="headerlink" title="动态代理的原理："></a><strong>动态代理的原理：</strong></h3><p>​    动态代理由程序在内存中动态生成一个对象，不需要我们手写代理对象，我们只需要指定代理方法的模板即可。</p>


<h3 id="JDK自带动态代理的核心类："><a href="#JDK自带动态代理的核心类：" class="headerlink" title="JDK自带动态代理的核心类："></a><strong>JDK自带动态代理的核心类：</strong></h3><h4 id="java-lang-reflect-Proxy类："><a href="#java-lang-reflect-Proxy类：" class="headerlink" title="java.lang.reflect.Proxy类："></a>java.lang.reflect.<strong>Proxy类</strong>：</h4><p>​     该类中的newInstance的静态方法，用于创建动态代理对象，该类也是所有生成的动态代理对象的父类。</p>
<p>​    <strong>核心方法：</strong></p>
<p>​     Proxy.<strong>newProxyInstance</strong>(ClassLoader loader, Class&lt;?&gt;[] interfaces, InvocationHandler h)</p>
<p>​     <strong>参数：</strong></p>
<ul>
<li><p>ClassLoader：传入被代理类的类加载器，实际上只需要传入类加载器即可，不一定必须传入“被代理类”的类加载器，也可以传入别的实例的类加载器，因为java中类加载器是一个对象。这个类加载器用于将生成的字节码文件加载进方法区，并生成字节码对象。</p>
</li>
<li><p>Class&lt;?&gt;[] interfaces：这个参数是指定代理对象实现的接口，可以实现多个接口，这些接口中的所有方法都会按照invoke模板中的代码进行加强。</p>
</li>
<li><p>InvocationHandler h：传入InvocationHandler接口的实现类，该类中的invoke方法是代理对象中所有方法的逻辑处理模板。</p>
</li>
</ul>
<h4 id="java-lang-reflect-InvocationHandler接口："><a href="#java-lang-reflect-InvocationHandler接口：" class="headerlink" title="java.lang.reflect.InvocationHandler接口："></a>java.lang.reflect.<strong>InvocationHandler</strong>接口：</h4><p>​    该接口有一个invoke方法，我们需要实现invoke方法，这个方法就是代理方法的模板。</p>
<p>​    <strong>接口中需要实现的方法：</strong></p>
<p>​     <strong>invoke</strong>(Object proxy, Method method, Object[] args)；</p>
<p>​    <strong>参数：</strong></p>
<ul>
<li><p>Object proxy：代表代理对象本身，可以它调用代理对象的其他方法</p>
</li>
<li><p>Method method：代表“被代理对象”对应方法的字节码对象</p>
</li>
<li><p>Object[] args：传入“代理对象”对应方法的参数数组</p>
</li>
</ul>
<h3 id="动态代理的示例代码："><a href="#动态代理的示例代码：" class="headerlink" title="动态代理的示例代码："></a><strong>动态代理的示例代码：</strong></h3><figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ServiceInvocationHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">InvocationHandler</span> </span>&#123;</span><br><span class="line">    <span class="hljs-keyword">private</span> Object instance;<span class="hljs-comment">//这是目标对象（被代理对象）</span></span><br><span class="line"></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">getInstance</span><span class="hljs-params">()</span> </span>&#123;</span><br><span class="line">        <span class="hljs-keyword">return</span> instance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setInstance</span><span class="hljs-params">(Object instance)</span> </span>&#123;</span><br><span class="line">        <span class="hljs-keyword">this</span>.instance = instance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">    <span class="hljs-comment">/***</span></span><br><span class="line"><span class="hljs-comment">     * proxy:代表代理对象本身,可以通过它调用代理对象的其他方法</span></span><br><span class="line"><span class="hljs-comment">     * method:代表目标对象对应方法的字节码对象</span></span><br><span class="line"><span class="hljs-comment">     * args：代表目标对象相应的参数</span></span><br><span class="line"><span class="hljs-comment">     */</span></span><br><span class="line">    <span class="hljs-meta">@Override</span></span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">invoke</span><span class="hljs-params">(Object proxy, Method method, Object[] args)</span> <span class="hljs-keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">        System.out.println(<span class="hljs-string">"开启事务"</span>);</span><br><span class="line">        method.invoke(instance,args);</span><br><span class="line">        System.out.println(<span class="hljs-string">"提交事务"</span>);</span><br><span class="line">        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>​      通过Proxy类创建动态代理:</p>
<figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestMyProxy</span> </span>&#123;</span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception</span>&#123;</span><br><span class="line">    	<span class="hljs-comment">//创建目标对象</span></span><br><span class="line">        ServiceInterface sale = <span class="hljs-keyword">new</span> ServiceImpl();</span><br><span class="line">        <span class="hljs-comment">//创建模板对象</span></span><br><span class="line">        ServiceInvocationHandler invocationHandler = <span class="hljs-keyword">new</span> ServiceInvocationHandler();</span><br><span class="line">        <span class="hljs-comment">//将目标对象注入模板对象中</span></span><br><span class="line">        invocationHandler.setInstance(sale);</span><br><span class="line">        </span><br><span class="line">        Class serviceClazz = sale.getClass();</span><br><span class="line">        ServiceInterface proxy = (ServiceInterface) Proxy.newProxyInstance(serviceClazz.getClassLoader(), serviceClazz.getInterfaces(), invocationHandler);</span><br><span class="line">        </span><br><span class="line">        proxy.a();</span><br><span class="line">        proxy.b();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过测试，动态代理对象与前面的静态代理结果相同:</p>




<h2 id="JDK动态代理的原理"><a href="#JDK动态代理的原理" class="headerlink" title="JDK动态代理的原理"></a><strong>JDK动态代理的原理</strong></h2><p>​    看到这里肯定很多人都会另一头雾水，动态代理底层到底是怎么实现的，我们想办法通过程序将代理对象字节码文件输出到磁盘上，然后通过jdgui反编译工具，查看动态代理对象的源码结构。</p>
<figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestMyProxy</span> </span>&#123;</span><br><span class="line">    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception</span>&#123;</span><br><span class="line">        <span class="hljs-keyword">byte</span>[] buffer = ProxyGenerator.generateProxyClass(<span class="hljs-string">"$Proxy0"</span>, <span class="hljs-keyword">new</span> Class[]&#123;ServiceInterface.class&#125;);</span><br><span class="line">        <span class="hljs-keyword">try</span> &#123;</span><br><span class="line">            FileOutputStream output = <span class="hljs-keyword">new</span> FileOutputStream(<span class="hljs-string">"C:/Users/Lenovo/Desktop/$Proxy0.class"</span>);</span><br><span class="line">            output.write(buffer);</span><br><span class="line">        &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注：使用ProxyGenerator类需要将JRE中lib目录下的一个jar包导入到项目中</p>


<p>通过反编译工具（推荐jd-gui）可以看到代理对象源码结构如下（下列代码对源码进行了部分删减，如果有需要可以自行生成源代码查看）：</p>
<figure class="highlight java hljs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> $<span class="hljs-title">Proxy0</span></span></span><br><span class="line"><span class="hljs-class">  <span class="hljs-keyword">extends</span> <span class="hljs-title">Proxy</span></span></span><br><span class="line"><span class="hljs-class">  <span class="hljs-keyword">implements</span> <span class="hljs-title">ServiceInterface</span></span></span><br><span class="line"><span class="hljs-class"></span>&#123;</span><br><span class="line">  <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Method m1;</span><br><span class="line">  <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Method m4;</span><br><span class="line">  <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Method m3;</span><br><span class="line">  <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Method m2;</span><br><span class="line">  <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Method m0;</span><br><span class="line">  </span><br><span class="line">  <span class="hljs-keyword">public</span> $Proxy0(InvocationHandler paramInvocationHandler)</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="hljs-keyword">super</span>(paramInvocationHandler);</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line"> </span><br><span class="line">  </span><br><span class="line">  <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">b</span><span class="hljs-params">()</span></span></span><br><span class="line"><span class="hljs-function">  </span>&#123;</span><br><span class="line">    <span class="hljs-keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="hljs-keyword">this</span>.h.invoke(<span class="hljs-keyword">this</span>, m4, <span class="hljs-keyword">null</span>);</span><br><span class="line">      <span class="hljs-keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="hljs-keyword">catch</span> (Error|RuntimeException localError)</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="hljs-keyword">throw</span> localError;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="hljs-keyword">catch</span> (Throwable localThrowable)</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UndeclaredThrowableException(localThrowable);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">a</span><span class="hljs-params">()</span></span></span><br><span class="line"><span class="hljs-function">  </span>&#123;</span><br><span class="line">    <span class="hljs-keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">      <span class="hljs-keyword">this</span>.h.invoke(<span class="hljs-keyword">this</span>, m3, <span class="hljs-keyword">null</span>);</span><br><span class="line">      <span class="hljs-keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="hljs-keyword">catch</span> (Error|RuntimeException localError)</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="hljs-keyword">throw</span> localError;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="hljs-keyword">catch</span> (Throwable localThrowable)</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UndeclaredThrowableException(localThrowable);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="hljs-keyword">static</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="hljs-keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">      m1 = Class.forName(<span class="hljs-string">"java.lang.Object"</span>).getMethod(<span class="hljs-string">"equals"</span>, <span class="hljs-keyword">new</span> Class[] &#123; Class.forName(<span class="hljs-string">"java.lang.Object"</span>) &#125;);</span><br><span class="line">      m4 = Class.forName(<span class="hljs-string">"com.proxy.ServiceInterface"</span>).getMethod(<span class="hljs-string">"b"</span>, <span class="hljs-keyword">new</span> Class[<span class="hljs-number">0</span>]);</span><br><span class="line">      m3 = Class.forName(<span class="hljs-string">"com.proxy.ServiceInterface"</span>).getMethod(<span class="hljs-string">"a"</span>, <span class="hljs-keyword">new</span> Class[<span class="hljs-number">0</span>]);</span><br><span class="line">      m2 = Class.forName(<span class="hljs-string">"java.lang.Object"</span>).getMethod(<span class="hljs-string">"toString"</span>, <span class="hljs-keyword">new</span> Class[<span class="hljs-number">0</span>]);</span><br><span class="line">      m0 = Class.forName(<span class="hljs-string">"java.lang.Object"</span>).getMethod(<span class="hljs-string">"hashCode"</span>, <span class="hljs-keyword">new</span> Class[<span class="hljs-number">0</span>]);</span><br><span class="line">      <span class="hljs-keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="hljs-keyword">catch</span> (NoSuchMethodException localNoSuchMethodException)</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NoSuchMethodError(localNoSuchMethodException.getMessage());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="hljs-keyword">catch</span> (ClassNotFoundException localClassNotFoundException)</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NoClassDefFoundError(localClassNotFoundException.getMessage());</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们分析动态生成的代理类源码可知，代理对象中的代理方法，都执行了this.h.invoke()方法，this.h实际上是代理类的父类中（Proxy类）中的属性，它保存了在调用Proxy.newInstance()方法时传入的InvocationHandler实例，而InvocationHandler中的invoke方法就是，代理类的代理逻辑。</p>


<p> 也就是说在<code>生成的一个动态代理对象中，对目标对象中所有方法都进行了相同的前处理和后处理过程，因为都执行相同的invoke“模板”方法</code>，也就是说如果我们想对目标对象中每个方法进行不同的前处理和后处理，我们需要编写不同的InvocationHandler实现类，然后创建不同的动态代理对象才能实现这一需求。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a><strong>总结</strong></h2><p>动态代理实际上就是在内存中直接生成字节码文件（通常程序员写的java文件编译后生成的字节码文件都在硬盘中），然后将字节码加载进方法区生成字节码对象，生成字节码对象后通过反射就可以创建代理对象的实例了。生成字节码文件就必定需要生成这个代理类的方法，那程序是怎么知道这个代理对象需要实现哪些方法呢，这就是为什么在Proxy.newProxyInstance()需要传入接口的数组，传入几个接口，这个代理就会实现这些接口，程序自然就知道它需要实现哪些方法了。此时就会开始循环生成代理类中的方法，那这个方法的具体实现代码又是什么呢？由于生成的代理类继承了Proxy类，在Proxy类中有一个h字段，保存的是一个InvocationHandler实现类的对象，而我们通过Proxy.newInstance()方法时，传入了一个InvocationHandler实例，所以在代理对象中存放着一个InvocationHandler对象，代理对象的每个方法都会调用父类中存放的InvocationHandler实例中的invoke方法。</p>
<h2 id="说明"><a href="#说明" class="headerlink" title="说明"></a><strong>说明</strong></h2><p>本文由听到微笑CSDN博客移植而来，原文地址： <a href="https://blog.csdn.net/tianjindong0804/article/details/84072096" target="_blank" rel="noopener">原文</a></p>

        </div>
        
        <div class="level is-size-7 is-uppercase">
            <div class="level-start">
                <div class="level-item">
                    <span class="is-size-6 has-text-grey has-mr-7">#</span>
                    <a class="has-link-grey -link" href="/tags/代理模式/">代理模式</a>, <a class="has-link-grey -link" href="/tags/设计模式/">设计模式</a>
                </div>
            </div>
        </div>
        
        
        
        
<div class="sharethis-inline-share-buttons"></div>
<script type="text/javascript" src="//platform-api.sharethis.com/js/sharethis.js#property=5d1b4ddd55a8b200110cdf62&amp;product=social-ab" async="async"></script>

        
    </div>
</div>



<div class="card">
    <div class="card-content">
        <h3 class="menu-label has-text-centered">喜欢这篇文章？打赏一下作者吧</h3>
        <div class="buttons is-centered">
            
                
<a class="button is-info donate">
    <span class="icon is-small">
        <i class="fab fa-alipay"></i>
    </span>
    <span>支付宝</span>
    <div class="qrcode"><img src="/images/alipay.jpg" alt="支付宝"></div>
</a>

                
                
<a class="button is-success donate">
    <span class="icon is-small">
        <i class="fab fa-weixin"></i>
    </span>
    <span>微信</span>
    <div class="qrcode"><img src="/images/wechat.png" alt="微信"></div>
</a>

                
        </div>
    </div>
</div>



<div class="card card-transparent">
    <div class="level post-navigation is-flex-wrap is-mobile">
        
        <div class="level-end">
            <a class="level level-item has-link-grey  article-nav-next" href="/article/22550.html">
                <i class="level-item fas fa-chevron-left"></i>
                <span class="level-item">原型模式</span>
            </a>
        </div>
        
        
        <div class="level-start">
            <a class="level level-item has-link-grey  article-nav-prev" href="/article/57476.html">
                <span class="level-item">cglib代理</span>
                <i class="level-item fas fa-chevron-right"></i>
            </a>
        </div>
        
    </div>
</div>



<div class="card">
    <div class="card-content">
        <h3 class="title is-5 has-text-weight-normal">评论</h3>
        
<div id="comment-container"></div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1.4.1/dist/gitalk.css">
<script src="https://cdn.jsdelivr.net/npm/gitalk@1.4.1/dist/gitalk.min.js"></script>
<script>
    var gitalk = new Gitalk({
        clientID: 'a362d7e659d5da0b4c24',
        clientSecret: 'bfd2817b484ba4d45ad14e44bbf993fa353a4f2d',
        id: '2afe4943e6bb54e37fcd41c7542d02dc',
        repo: 'blog_comment',
        owner: 'tianjindong',
        admin: "tianjindong"
    })
    gitalk.render('comment-container')
</script>

    </div>
</div>
</div>
                




<div class="column is-4-tablet is-3-desktop is-3-widescreen  has-order-1 column-left ">
    
        
<div class="card widget">
    <div class="card-content">
        <nav class="level">
            <div class="level-item has-text-centered">
                <div>
                    
                        <img class="image is-128x128 has-mb-6" src="/images/avatar.png" alt="听到微笑">
                    
                    
                    <p class="is-size-4 is-block">
                        听到微笑
                    </p>
                    
                    
                    <p class="is-size-6 is-block">
                        Java Engineer
                    </p>
                    
                    
                    <p class="is-size-6 is-flex is-flex-center has-text-grey">
                        <i class="fas fa-map-marker-alt has-mr-7"></i>
                        <span>中国 上海</span>
                    </p>
                    
                </div>
            </div>
        </nav>
        <nav class="level is-mobile">
            <div class="level-item has-text-centered is-marginless">
                <div>
                    <p class="heading">
                        文章
                    </p>
                    <p class="title has-text-weight-normal">
                        54
                    </p>
                </div>
            </div>
            <div class="level-item has-text-centered is-marginless">
                <div>
                    <p class="heading">
                        分类
                    </p>
                    <p class="title has-text-weight-normal">
                        20
                    </p>
                </div>
            </div>
            <div class="level-item has-text-centered is-marginless">
                <div>
                    <p class="heading">
                        标签
                    </p>
                    <p class="title has-text-weight-normal">
                        30
                    </p>
                </div>
            </div>
        </nav>
        <div class="level">
            <a class="level-item button is-link is-rounded" href="https://github.com/tianjindong" target="_blank">
                关注我</a>
        </div>
        
        
        <div class="level is-mobile">
            
            <a class="level-item button is-white is-marginless" target="_blank" title="Github" href="https://github.com/tianjindong">
                
                <i class="iconfont-github icon-GitHub"></i>
                
            </a>
            
            <a class="level-item button is-white is-marginless" target="_blank" title="Gitee" href="https://gitee.com/tianjindong">
                
                <i class="iconfont-gitee icon-gitee"></i>
                
            </a>
            
            <a class="level-item button is-white is-marginless" target="_blank" title="CSDN" href="https://blog.csdn.net/tianjindong0804">
                
                <i class="iconfont-csdn icon-CN_csdnnet"></i>
                
            </a>
            
            <a class="level-item button is-white is-marginless" target="_blank" title="Twitter" href="https://twitter.com/HearingSmile">
                
                <i class="iconfont-twitter icon-twitter"></i>
                
            </a>
            
        </div>
        
    </div>
</div>
    
        
<div class="card widget" id="toc">
    <div class="card-content">
        <div class="menu">
            <h3 class="menu-label">
                目录
            </h3>
            <ul class="menu-list"><li>
        <a class="is-flex" href="#静态代理">
        <span class="has-mr-6">1</span>
        <span>静态代理</span>
        </a></li><li>
        <a class="is-flex" href="#满足代理模式应用场景">
        <span class="has-mr-6">2</span>
        <span>满足代理模式应用场景</span>
        </a></li><li>
        <a class="is-flex" href="#静态代理的优点及缺点">
        <span class="has-mr-6">3</span>
        <span>静态代理的优点及缺点</span>
        </a></li><li>
        <a class="is-flex" href="#动态代理">
        <span class="has-mr-6">4</span>
        <span>动态代理</span>
        </a><ul class="menu-list"><li>
        <a class="is-flex" href="#动态代理的原理：">
        <span class="has-mr-6">4.1</span>
        <span>动态代理的原理：</span>
        </a></li><li>
        <a class="is-flex" href="#JDK自带动态代理的核心类：">
        <span class="has-mr-6">4.2</span>
        <span>JDK自带动态代理的核心类：</span>
        </a><ul class="menu-list"><li>
        <a class="is-flex" href="#java-lang-reflect-Proxy类：">
        <span class="has-mr-6">4.2.1</span>
        <span>java.lang.reflect.Proxy类：</span>
        </a></li><li>
        <a class="is-flex" href="#java-lang-reflect-InvocationHandler接口：">
        <span class="has-mr-6">4.2.2</span>
        <span>java.lang.reflect.InvocationHandler接口：</span>
        </a></li></ul></li><li>
        <a class="is-flex" href="#动态代理的示例代码：">
        <span class="has-mr-6">4.3</span>
        <span>动态代理的示例代码：</span>
        </a></li></ul></li><li>
        <a class="is-flex" href="#JDK动态代理的原理">
        <span class="has-mr-6">5</span>
        <span>JDK动态代理的原理</span>
        </a></li><li>
        <a class="is-flex" href="#总结">
        <span class="has-mr-6">6</span>
        <span>总结</span>
        </a></li><li>
        <a class="is-flex" href="#说明">
        <span class="has-mr-6">7</span>
        <span>说明</span>
        </a></li></ul>
        </div>
    </div>
</div>

    
        
    
    
        <div class="column-right-shadow  is-sticky">
        
            
<div class="card widget">
    <div class="card-content">
        <h3 class="menu-label">
            最新文章
        </h3>
        
        <article class="media">
            
            <a href="/article/37883.html" class="media-left">
                <p class="image is-64x64">
                    <img class="thumbnail" src="http://image.bigcoder.cn/20210116215841.png" alt="DockerSwarm架构详解(转载)">
                </p>
            </a>
            
            <div class="media-content">
                <div class="content">
                    <div><time class="has-text-grey is-size-7 is-uppercase" datetime="2021-01-16T13:46:04.000Z">2021-01-16</time></div>
                    <a href="/article/37883.html" class="has-link-black-ter is-size-6">DockerSwarm架构详解(转载)</a>
                    <p class="is-size-7 is-uppercase">
                        <a class="has-link-grey -link" href="/categories/Docker/">Docker</a>
                    </p>
                </div>
            </div>
        </article>
        
        <article class="media">
            
            <a href="/article/54308.html" class="media-left">
                <p class="image is-64x64">
                    <img class="thumbnail" src="/gallery/thumbnails/h4.jpg" alt="B树和B+树与磁盘读写之间的联系">
                </p>
            </a>
            
            <div class="media-content">
                <div class="content">
                    <div><time class="has-text-grey is-size-7 is-uppercase" datetime="2019-08-24T02:20:31.000Z">2019-08-24</time></div>
                    <a href="/article/54308.html" class="has-link-black-ter is-size-6">B树和B+树与磁盘读写之间的联系</a>
                    <p class="is-size-7 is-uppercase">
                        <a class="has-link-grey -link" href="/categories/数据结构/">数据结构</a>
                    </p>
                </div>
            </div>
        </article>
        
        <article class="media">
            
            <a href="/article/21035.html" class="media-left">
                <p class="image is-64x64">
                    <img class="thumbnail" src="/gallery/thumbnails/h3.jpg" alt="什么是B+树">
                </p>
            </a>
            
            <div class="media-content">
                <div class="content">
                    <div><time class="has-text-grey is-size-7 is-uppercase" datetime="2019-08-19T10:25:05.000Z">2019-08-19</time></div>
                    <a href="/article/21035.html" class="has-link-black-ter is-size-6">什么是B+树</a>
                    <p class="is-size-7 is-uppercase">
                        <a class="has-link-grey -link" href="/categories/数据结构/">数据结构</a>
                    </p>
                </div>
            </div>
        </article>
        
        <article class="media">
            
            <a href="/article/8567.html" class="media-left">
                <p class="image is-64x64">
                    <img class="thumbnail" src="/gallery/thumbnails/f6.jpg" alt="什么是B-树">
                </p>
            </a>
            
            <div class="media-content">
                <div class="content">
                    <div><time class="has-text-grey is-size-7 is-uppercase" datetime="2019-08-19T10:24:50.000Z">2019-08-19</time></div>
                    <a href="/article/8567.html" class="has-link-black-ter is-size-6">什么是B-树</a>
                    <p class="is-size-7 is-uppercase">
                        <a class="has-link-grey -link" href="/categories/数据结构/">数据结构</a>
                    </p>
                </div>
            </div>
        </article>
        
        <article class="media">
            
            <a href="/article/62369.html" class="media-left">
                <p class="image is-64x64">
                    <img class="thumbnail" src="/gallery/thumbnails/h1.jpg" alt="什么是一致性Hash算法">
                </p>
            </a>
            
            <div class="media-content">
                <div class="content">
                    <div><time class="has-text-grey is-size-7 is-uppercase" datetime="2019-08-16T07:27:10.000Z">2019-08-16</time></div>
                    <a href="/article/62369.html" class="has-link-black-ter is-size-6">什么是一致性Hash算法</a>
                    <p class="is-size-7 is-uppercase">
                        <a class="has-link-grey -link" href="/categories/数据结构/">数据结构</a>
                    </p>
                </div>
            </div>
        </article>
        
    </div>
</div>

        
            
        
        </div>
    
</div>

                
            </div>
        </div>
    </section>
    <footer class="footer">
    <div class="container">
        <div class="level">
            <div class="level-start has-text-centered-mobile">
                <a class="footer-logo is-block has-mb-6" href="/">
                
                    <img src="/images/logo.png" alt="JDK动态代理的深入理解" height="28">
                
                </a>
                <p class="is-size-7">
                &copy; 2021 听到微笑&nbsp;
                Powered by <a href="https://hexo.io/" target="_blank">Hexo</a> & <a href="https://github.com/ppoffice/hexo-theme-icarus" target="_blank">Icarus</a>
                
                </p>
            </div>
            <div class="level-end">
            
            </div>
        </div>
    </div>
</footer>
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@2.22.2/min/moment-with-locales.min.js"></script>
<script>moment.locale("zh-CN");</script>


    
    
    
    <script src="/js/animation.js"></script>
    

    
    
    
    <script src="https://cdn.jsdelivr.net/npm/lightgallery@1.6.8/dist/js/lightgallery.min.js" defer></script>
    <script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js" defer></script>
    <script src="/js/gallery.js" defer></script>
    

    
    

<div id="outdated">
    <h6>Your browser is out-of-date!</h6>
    <p>Update your browser to view this website correctly. <a id="btnUpdateBrowser" href="http://outdatedbrowser.com/">Update
            my browser now </a></p>
    <p class="last"><a href="#" id="btnCloseUpdateBrowser" title="Close">&times;</a></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/outdatedbrowser@1.1.5/outdatedbrowser/outdatedbrowser.min.js" defer></script>
<script>
    document.addEventListener("DOMContentLoaded", function () {
        outdatedBrowser({
            bgColor: '#f25648',
            color: '#ffffff',
            lowerThan: 'flex'
        });
    });
</script>


    
    
<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.5/unpacked/MathJax.js?config=TeX-MML-AM_CHTML" defer></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
    MathJax.Hub.Config({
        'HTML-CSS': {
            matchFontHeight: false
        },
        SVG: {
            matchFontHeight: false
        },
        CommonHTML: {
            matchFontHeight: false
        },
        tex2jax: {
            inlineMath: [
                ['$','$'],
                ['\\(','\\)']
            ]
        }
    });
});
</script>

    
    

<a id="back-to-top" title="回到顶端" href="javascript:;">
    <i class="fas fa-chevron-up"></i>
</a>
<script src="/js/back-to-top.js" defer></script>


    
    

    
    
    
    

    
    
    
    
    
    <script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.4/dist/clipboard.min.js" defer></script>
    <script src="/js/clipboard.js" defer></script>
    

    
    
    


<script src="/js/main.js" defer></script>

    
    <div class="searchbox ins-search">
    <div class="searchbox-container ins-search-container">
        <div class="searchbox-input-wrapper">
            <input type="text" class="searchbox-input ins-search-input" placeholder="想要查找什么...">
            <span class="searchbox-close ins-close ins-selectable"><i class="fa fa-times-circle"></i></span>
        </div>
        <div class="searchbox-result-wrapper ins-section-wrapper">
            <div class="ins-section-container"></div>
        </div>
    </div>
</div>
<script>
    (function (window) {
        var INSIGHT_CONFIG = {
            TRANSLATION: {
                POSTS: '文章',
                PAGES: '页面',
                CATEGORIES: '分类',
                TAGS: '标签',
                UNTITLED: '(无标题)',
            },
            CONTENT_URL: '/content.json',
        };
        window.INSIGHT_CONFIG = INSIGHT_CONFIG;
    })(window);
</script>
<script src="/js/insight.js" defer></script>
<link rel="stylesheet" href="/css/search.css">
<link rel="stylesheet" href="/css/insight.css">
    
</body>
</html>